package cn.gjxblog.transaction;

import cn.gjxblog.base.ThreadUtils;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.sql.DataSource;
import java.util.List;

/**
 * 事务同步器测试
 *
 * 场景：主线程插入数据、异步线程需要查询出这个事务。
 *
 * 如果想在事务提交后，进行异步操作需要事务同步器
 *
 */
public class TransactionSynchronizationTests {

    AnnotationConfigApplicationContext context;
    @Before
    public void before(){
        context = new AnnotationConfigApplicationContext();
        context.register(AnnotationTransactionTests.RootConfig.class, TransactionSynchronizationTestsConfig.class);
        context.refresh();
    }


    @Test
    public void test(){
        UserService userService = context.getBean(UserService.class);
        userService.addUser3();
    }


    @Configuration
    @EnableTransactionManagement
    static class TransactionSynchronizationTestsConfig{

        @Bean
        public JdbcTemplate jdbcTemplate(DataSource dataSource){
           return new JdbcTemplate(dataSource);
        }

        @Bean
        public UserService userService(){
            return new UserService();
        }


    }


    @Service
    static class UserService{
        @Autowired
        JdbcTemplate jdbcTemplate;

        /**
         * 在这种情况下，当事务没有提交前，事务内另外开一个线程去获取结果是获取不到的。
         * 为了解决需要事务提交后再操作，Spring引入了事务同步化器
         *  TransactionSynchronization
         */
        @Transactional
        public void addUser(){
            jdbcTemplate.execute("insert into user (`name`,`age`) values ('JuLi',22)");
            new Thread(() -> {
                List<String> result = jdbcTemplate.queryForList("select name from user Where name ='JuLi'", String.class);
                if(result.isEmpty()) {
                    System.out.println("查询结果为空");
                }else {
                    result.forEach(System.out::println);
                }
            }).start();

            ThreadUtils.sleep(10);//放大场景
        }

        /**
         * 通过TransactionSynchronizationManager注册TransactionSynchronization完成上述的场景
         */
        @Transactional
        public void addUser2(){
            jdbcTemplate.execute("insert into user (`name`,`age`) values ('JuLi',22)");

            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
                @Override
                public void beforeCompletion() {
                    List<String> result = jdbcTemplate.queryForList("select name from user Where name ='JuLi'", String.class);
                    if(result.isEmpty()) {
                        System.out.println("查询结果为空");
                    }else {
                        result.forEach(System.out::println);
                    }
                }
            });
            ThreadUtils.sleep(10);//放大场景
        }

        /**
         * Spring4.2 引入了事件机制来解决事务提交同步的问题。
         * @see TransactionalEventListenerFactory
         */

        @Autowired
        ApplicationEventMulticaster applicationEventMulticaster;

        @Transactional
        public void addUser3(){
            jdbcTemplate.execute("insert into user (`name`,`age`) values ('JuLi',22)");
            applicationEventMulticaster.multicastEvent(new QueryEvent(this));
            ThreadUtils.sleep(10);//放大场景
        }

        @TransactionalEventListener(value =QueryEvent.class,phase = TransactionPhase.BEFORE_COMMIT)
        public void eventListener(Object o){
            System.out.println(o);
            List<String> result = jdbcTemplate.queryForList("select name from user Where name ='JuLi'", String.class);
            if(result.isEmpty()) {
                System.out.println("查询结果为空");
            }else {
                result.forEach(System.out::println);
            }
        }

        static class QueryEvent extends ApplicationEvent {
            /**
             * Create a new ApplicationEvent.
             *
             * @param source the object on which the event initially occurred (never {@code null})
             */
            public QueryEvent(Object source) {
                super(source);
            }
        }
    }
}
